extern crate ferris_says;

use clap::{self, App, Arg};
use ferris_says::say;
use std::{collections::HashSet, fs, io::stdout, io::BufWriter, io::Write};

fn main() {
    let matches = App::new("Inc 0")
        .version("0.3")
        .author("刘凌远 <liu_lingyuan@icloud.com>")
        .about("Add nonsenses behind jump instructions (＠_＠;)")
        .arg(
            Arg::with_name("INPUT")
                .help("Set the input file to use")
                .required(true)
                .index(1),
        )
        .arg(
            Arg::with_name("OUTPUT")
                .short("o")
                .help("Set the name of output file")
                .required(true)
                .takes_value(true),
        )
        .get_matches();

    let dic: HashSet<&str> = vec![
        "j", "jal", "jr", "jalr", "beq", "bne", "bgez", "bgtz", "blez", "bltz", "bgezal", "bltzal",
    ]
    .into_iter()
    .collect();

    let path = matches.value_of("INPUT").unwrap();
    let contents =
        fs::read_to_string(path).expect(format!("Fail to open the file {}", path).as_str());
    let mut lines: Vec<&str> = contents
        .split(|c| c == '\n' || c == '\r')
        .filter(|s| !s.is_empty())
        .collect();

    let mut positions = Vec::new();

    for (i, line) in lines.iter().enumerate() {
        // Replace \t to space
        let replaced = line.replace("\t", " ");
        let line = replaced.as_str();
        // remove labels
        let instruction = match line.split_once(':') {
            Some(pair) => pair.1,
            None => line,
        };
        // get the first word
        if let Some(pair) = instruction.split_once(' ') {
            let command = pair.0.to_lowercase();
            if dic.contains(command.as_str()) {
                positions.push(i);
            }
        }
    }

    for i in positions.iter().rev() {
        lines.insert(i + 1, "sll $0, $0, 0");
    }

    let output = matches.value_of("OUTPUT").unwrap();
    let _ = fs::File::create(output)
        .expect("Fail to create new file.")
        .write(lines.join("\n").as_bytes());

    let out = b"Done!";
    let width = 24;

    let mut writer = BufWriter::new(stdout());
    say(out, width, &mut writer).unwrap();
}
